package com.biz.primus.ms.homepage.service;

import com.biz.primus.base.enums.CommonStatus;
import com.biz.primus.common.utils.DateUtil;
import com.biz.primus.homepage.enums.CoverClientType;
import com.biz.primus.homepage.enums.ElementsLoadType;
import com.biz.primus.homepage.vo.*;
import com.biz.primus.ms.base.service.AbstractBaseService;
import com.biz.primus.ms.homepage.dao.redis.CoverRedisDao;
import com.biz.primus.ms.homepage.dao.ro.CoverRo;
import com.biz.primus.ms.homepage.helper.BeanDiscoverer;
import com.biz.primus.ms.homepage.helper.MethodExecutable;
import com.biz.primus.ms.homepage.helper.MethodExecutor;
import com.biz.primus.ms.homepage.helper.SimpleCacheHelper;
import com.biz.primus.ms.homepage.trans.CoverRo2CoverRoVo;
import com.biz.primus.ms.homepage.trans.ProgramRoVo2ProgramUpdateReqVo;
import com.google.common.cache.CacheLoader;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.TimeUnit;

import static com.google.common.collect.Lists.newArrayList;
import static com.google.common.collect.Maps.newHashMap;
import static org.apache.commons.collections.CollectionUtils.isNotEmpty;

/**
 * Created by jiangnan on 2017/7/12.
 */
@Slf4j
@Service
public class CoverServerServiceImpl extends AbstractBaseService implements CoverServerService {

    @Autowired
    private CoverRedisDao coverRedisDao;

    @Autowired
    private ProgramServerService programServerService;


    @Override
    public List<CoverRoVo> findAll() {
        List<CoverRo> covers = coverRedisDao.findAll();
        Collections.sort(covers);
        return Lists.transform(covers, new CoverRo2CoverRoVo());
    }

    @Override
    public CoverUpdateVo saveCover(CoverUpdateVo reqVo) {
        log.info("saveCover reqVo:{}",reqVo);
        CoverRo ro = coverRedisDao.findOne(reqVo.getId());
        if (ro == null) {
            log.info("create cover");
            ro = new CoverRo();
            ro.setId(reqVo.getId());
        }
        ro.setName(reqVo.getName());
        ro.setDescription(reqVo.getDescription());
        ro.setIndex(reqVo.getIndex());
        ro.setUrl(reqVo.getUrl());
        ro.setTemplate(reqVo.getTemplate());
        ro.setStatus(reqVo.getStatus());
        ro.setStartTime(reqVo.getStartTime());
        ro.setEndTime(reqVo.getEndTime());
        ro.setUpdateTimestamp(DateUtil.now());
        ro.setCoverClientType(reqVo.getCoverClientType());
        coverRedisDao.save(ro);
        reqVo.setId(ro.getId());
        return reqVo;
    }

    @Override
    public CoverRoVo findOne(String coverId) {
        CoverRo one = coverRedisDao.findOne(coverId);
        return new CoverRo2CoverRoVo().apply(one);
    }

    @Override
    public void deleteCover(CoverDeleteVo coverUpdateVo) {
        coverRedisDao.delete(coverUpdateVo.getCoverId());
        programServerService.deleteProgramsByCoverId(coverUpdateVo.getCoverId());
    }

    @Override
    public CoverRoVo getCover(CoverReqVo coverReqVo, CoverClientType coverClientType) {
        List<CoverRo> coverRos = coverRedisDao.findAll();
        Collection<CoverRo> matchedCovers = newArrayList();
        if (isNotEmpty(coverRos)) {
            for (CoverRo coverRo : coverRos) {
                if (Objects.equals(coverRo.getStatus(), CommonStatus.ENABLE) && coverRo.atTheRightTime()) {
                    //加入merge请求判断，使用merge模板
                    if (Objects.equals(coverRo.getCoverClientType(), coverClientType.getValue())) {
                        if (StringUtils.isNotBlank(coverReqVo.getCoverUrl())) {
                            if (Objects.equals(coverReqVo.getCoverUrl(), coverRo.getUrl())) {
                                matchedCovers.add(coverRo);
                            }
                        } else {
                            matchedCovers.add(coverRo);
                        }
                    }
                }
            }
            return getValidTopZIndexRo(matchedCovers);
        }
        return null;
    }

    @Override
    public CoverRoVo doClone(CoverWithPrograms coverWithPrograms, String clonedBy) {

        CoverRoVo cover = coverWithPrograms.getCover();
        String newCoverId = cover.getId();
        if (cover != null && cover.getId() != null) {
            cover.setId(null);
            cover.setStatus(CommonStatus.DISABLE);
            cover.setDescription(cover.getDescription() + ",由 " + clonedBy + " 克隆于 " + DateUtil
                    .getDateFormat(new Date(), "yyyy年M月d日 HH:mm:ss"));
            CoverUpdateVo coverUpdateVo = new CoverUpdateVo(cover);
            newCoverId = saveCover(coverUpdateVo).getId();
            cover.setId(newCoverId);
        }
        Map<Long, Long> oldProgramIdToNewId = newHashMap();
        if (CollectionUtils.isNotEmpty(coverWithPrograms.getPrograms())) {
            List<ProgramRoVo> newPrograms = newArrayList();
            for (ProgramRoVo programRo : coverWithPrograms.getPrograms()) {
                Long oldId = programRo.getId();
                programRo.setId(null);
                programRo.setCoverId(newCoverId);
                ProgramUpdateReqVo programVo = new ProgramRoVo2ProgramUpdateReqVo().apply(programRo);
                ProgramRoVo savedProgram = programServerService.save(programVo);
                newPrograms.add(savedProgram);
                oldProgramIdToNewId.put(oldId, savedProgram.getId());
            }
            for (ProgramRoVo programRo : newPrograms) {
                if (programRo.getParentId() != null) {
                    Long parentId = oldProgramIdToNewId.get(programRo.getParentId());
                    if (parentId == null) {
                        log.debug("Program[{}] parentId not found by oldId:[{}]", programRo.getName(), programRo.getParentId());
                        programServerService.delete(programRo.getId());
                    } else {
                        programRo.setParentId(parentId);
                        programServerService.save(new ProgramRoVo2ProgramUpdateReqVo().apply(programRo));
                    }
                }
            }
            log.debug("Success clone programs for cover[{}].");
        }
        return cover;
    }

    @Override
    public List<CoverProviderVo> listCoverProvider() {
        Collection<MethodExecutable> coverTemplateElementDataProviders = BeanDiscoverer.getCoverTemplateElementDataProviders();
        List<CoverProviderVo> resp = Lists.newArrayList();
        for (MethodExecutable index : coverTemplateElementDataProviders) {
            CoverProviderVo coverProviderVo = new CoverProviderVo();
            coverProviderVo.setDescription(index.getDescription());
            coverProviderVo.setIdentity(index.getIdentity());
            resp.add(coverProviderVo);
        }
        return resp;
    }


    private CoverRoVo getValidTopZIndexRo(Collection<CoverRo> coverRos) {
        CoverRo topRo = null;
        if (isNotEmpty(coverRos)) {
            for (CoverRo ro : coverRos) {
                topRo = topRo == null ? ro : topRo.getIndex() < ro.getIndex() ? ro : topRo;
            }
        }
        return new CoverRo2CoverRoVo().apply(topRo);
    }

    private void loadElementsImmediate(CoverTemplateData coverTemplateData, CoverReqVo location) {
        if (coverTemplateData != null) {
            coverTemplateData.setElements(this.getElements(coverTemplateData, location));
            coverTemplateData.setElementsLoadType(null);
            coverTemplateData.setMethodExecutor(null);
            coverTemplateData.setMethodExecutorMethodParam(null);
            if (MapUtils.isNotEmpty(coverTemplateData.getChildren())) {
                Collection<CoverTemplateData> children = coverTemplateData.getChildren().values();
                for (CoverTemplateData child : children) {
                    loadElementsImmediate(child, location);
                }
            }
        }
    }

    @Override
    public List<CoverTemplateData> getElements(CoverTemplateData coverTemplateData, CoverReqVo location) {
        if (coverTemplateData == null) {
            return Lists.newArrayList();
        }
        if (coverTemplateData.getElementsLoadType() == ElementsLoadType.DYNAMIC) {
            if (coverTemplateData.getDynamicElements() == null) {
                coverTemplateData.getMethodExecutorMethodParam().setLocation(location);
                CoverTemplateData tmp = new MethodExecutor(coverTemplateData.getMethodExecutor(), coverTemplateData.getMethodExecutorMethodParam()).execute();
                coverTemplateData.setDynamicElements(tmp == null ? null : this.getElements(tmp, location));
            }
            return coverTemplateData.getDynamicElements();
        }
        return coverTemplateData.getElements();
    }

    @Override
    public CoverTemplateData getCoverProgramData(CoverReqVo coverReqVo, CoverClientType coverClientType) {
        CoverRoVo coverRo = getCover(coverReqVo, coverClientType);
        if (coverRo == null) {
            return new CoverTemplateData();
        }
        coverReqVo.setCoverId(coverRo.getId());
        String cacheLoaderKey = coverClientType.getValue().toString();
        CoverTemplateData coverTemplateData = SimpleCacheHelper.loadFromCacheOrCacheLoader(getClass(), cacheLoaderKey,
                new CacheLoader<CoverReqVo, CoverTemplateData>() {
                    @Override
                    public CoverTemplateData load(CoverReqVo key) throws Exception {
                        CoverTemplateData coverTemplateData = programServerService.getCoverTemplateDataByCoverId(key.getCoverId(), false);
                        loadElementsImmediate(coverTemplateData, key);
                        return coverTemplateData;
                    }
                }, coverReqVo, TimeUnit.SECONDS, 10);
        return coverTemplateData;
    }

}
